package tcp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * TCP回显客户端
 * 功能：连接到服务器，发送用户输入并显示服务器响应
 */

public class TCPEchoClient {
    // 声明Socket对象，用于客户端与服务器之间通信并传输数据
    private Socket socket;

    /**
     * 构造方法
     * @param serverIp 服务器IP
     * @param serverPort 服务器端口号
     * @throws IOException
     */
    public TCPEchoClient (String serverIp, int serverPort) throws IOException {
        // 验证服务器IP地址的有效性
        if (serverIp == null || serverIp.isEmpty()) {
            throw new RuntimeException("Ip地址不能为空.");
        }
        // 验证服务器端口号的有效性
        if (serverPort < 1025 || serverPort > 65535) {
            throw new RuntimeException("端口号必须在1025 ~ 65535之间.");
        }
        // 根据服务器IP和端口号初始化Socket对象
        // 这个 new 操作完成之后, 就完成了 tcp 连接的建⽴
        this.socket = new Socket(serverIp, serverPort);
    }

    /**
     * 启动客户端，开始与服务器交互
     */
    public void start () {
        System.out.println("客户端已启动.");
        // 从Socket对象中获取输入与输出流对象
        try (InputStream inputStream = this.socket.getInputStream();
             OutputStream outputStream = this.socket.getOutputStream()) {
            // 1. 循环接收用户的输入，持续接收用户输入并发送到服务器
            while (true) {
                System.out.println("->");
                // 创建Scanner对象读取控制台输入
                Scanner scanner = new Scanner(System.in);
                // 读取用户输入的一行文本
                String request = scanner.nextLine();
                // 验证用户输入不为空
                if (request == null || request.isEmpty()) {
                    System.out.println("输入的内容不能为空.");
                    continue;// 跳过本次循环，重新提示输入
                }
                // 2. 把用户的输入封装到输出流中，可以用PrintWriter进行简化
                PrintWriter writer = new PrintWriter(outputStream);
                // 3. 把用户输入数据写入到输出流中，发送到服务器
                writer.println(request);
                // 4. 强制刷新缓冲区，确保数据立即发送，到此请求发送完成
                writer.flush();
                // 5. 创建Scanner对象读取服务器响应
                // 准备接收服务器返回的响应数据, 通过输入流创建用于读取响应的对象
                Scanner responseScanner = new Scanner(inputStream);
                // 6. 解析响应数据，读取服务器返回的一行响应
                String response = responseScanner.nextLine();
                // 7. 打印日志
                System.out.printf("request = %s, response = %s\n", request, response);
                // 关闭流之后表示客户端下线
//                scanner.close();

            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws IOException {
        // 创建客户端实例，连接到本地服务器的9999端口
        TCPEchoClient client = new TCPEchoClient("127.0.0.1", 9999);
        // 启动客户端
        client.start();
    }
}
